package eu.webhib.extgui.controller.list;

import eu.webhib.extgui.controller.intf.ListDataProvider;
import eu.webhib.extgui.data.intf.DataItem;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.primefaces.event.*;

/**
 * View controller managing a table list.
 */
public class ListController {

   private static final Logger log = Logger.getLogger(ListController.class);
   /** internal parameter */
   private static final boolean recursiveListChildren = true;
   private static final String defaultIndent = "   ";
   /** providing all data items */
   protected ListDataProvider dataProvider;
   /** list parent: all children (may be recursive) of this item are displayed in the list */
   private DataItem listParent;
   /** sequential list of data items displayed in table list */
   private List<DataItem> tableList;
   /** the currently selected item in the table list if any */
   private DataItem selectedItem;

   /**
    * @param dataProvider the data provider
    */
   public ListController(ListDataProvider dataProvider) {
      this.dataProvider = dataProvider;
   }

   /**
    * Called by outside (e.g. tree controller)
    * @return current list parent
    */
   public DataItem getListParent() {
      return listParent;
   }

   /**
    * Called by view
    * @return list of data items of the table list
    */
   public List<DataItem> getTableList() {
      return tableList;
   }

   /**
    * Called by outside (e.g tree controller) to change the list parent in order to update
    * the view. Table list is even rebuild and updated in view if list parent do not change
    * @param newListParent the new list parent
    */
   public void setListParent(DataItem newListParent) {
      listParent = newListParent;
      /** rebuild entries in table list */
      tableList = new ArrayList<DataItem>(20);
      if (recursiveListChildren) {
         log.info("setListParent: addToTableListRecursive");
         addToListRecursive(listParent);
      } else {
         tableList.addAll(dataProvider.getViewChildren(listParent));
      }
   }

   /**
    * Internal help to build table list recursively
    * @param listParent children of this are added recursively to tableList
    */
   private void addToListRecursive(DataItem listParent) {
      for (DataItem item : dataProvider.getViewChildren(listParent)) {
         tableList.add(item);
         addToListRecursive(item);
      }
   }

   /**
    * Get currently selected data item in table list.
    * Called by view and outside (e.g. tree controller)
    * @return currently selected data item
    */
   public DataItem getSelectedItem() {
      log.info("getSelectedItem: " + selectedItem);

      return selectedItem;
   }

   /**
    * Set the data item which has to be selected in the table list
    * Called by view and outside (e.g. tree controller)
    * @param selectedListItem data item to be selected in list
    */
   public void setSelectedItem(DataItem selectedListItem) {
      log.info("setSelectedItem - entry: " + selectedListItem);

      /** avoid deselection if user clicked on currently selected item */
      if (selectedListItem != null) {
         this.selectedItem = selectedListItem;
         log.info("setSelectedItem - set: " + selectedListItem);
      }
   }

   /**
    * Deselect if an item is selected
    * e.g. is called by tree controller if dummy root without data representation is
    * clicked by user to explicitly deselect the currently selected item
    */
   public void unselectItem() {
      log.info("unselectItem");

      selectedItem = null;
   }

   /**
    * Listener method called by view on item selection
    */
   public void onRowSelect(SelectEvent event) {
      log.info("onRowSelect: " + (DataItem) event.getObject());
   }

   /**
    * Listener method called by view on item deselection.
    * Need this in order to get notified about deselect events in order to avoid deselection
    * of currently selected item
    * @see setSelectedItem
    */
   public void onRowUnselect(UnselectEvent event) {
      log.info("onRowUnselect: " + (DataItem) event.getObject());
   }

   /**
    * Currently not used see below
    */
   public String levelIndent(DataItem item) {
      return levelIndent(item, null);
   }

   /**
    * Helper method used by view to get indention according to the child level relative
    * to the current list parent.
    * Does not work without the second parameter, throws exception in view (not clear why)
    * @param item data item whose indention has to be calculated
    * @param indent indention string (e.g. spaces) per child level
    * @return overall indention string for the given item
    */
   public String levelIndent(DataItem item, String indent) {
      log.trace("levelIndent: " + item + " listparent: " + listParent);
      assert (item != null);
      assert (item != listParent);
      if (indent == null) {
         //log.debug("levelIndent: indent==null");
         indent = defaultIndent;
      }
      //log.debug("levelIndent: " + item + " " + indent.length());
      item = dataProvider.getViewParent(item);
      StringBuilder builder = new StringBuilder(12);
      while (item != null && item != listParent) {
         builder.append(indent);
         item = dataProvider.getViewParent(item);
         //log.debug("levelIndent next parent: " + item + " " + builder.length());
      }
      //log.debug("levelIndent: " + builder.length());
      return builder.toString();
   }
}
